#ifndef THINK_THINK_DB_CONNECTION_H
#define THINK_THINK_DB_CONNECTION_H

static char* think_db_connection =
	"\n"
	"// +----------------------------------------------------------------------\n"
	"// | ThinkPHP [ WE CAN DO IT JUST THINK ]\n"
	"// +----------------------------------------------------------------------\n"
	"// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.\n"
	"// +----------------------------------------------------------------------\n"
	"// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )\n"
	"// +----------------------------------------------------------------------\n"
	"// | Author: liu21st <liu21st@gmail.com>\n"
	"// +----------------------------------------------------------------------\n"
	"\n"
	"namespace think\\db;\n"
	"\n"
	"use PDO;\n"
	"use PDOStatement;\n"
	"use think\\Db;\n"
	"use think\\db\\exception\\BindParamException;\n"
	"use think\\Debug;\n"
	"use think\\Exception;\n"
	"use think\\exception\\PDOException;\n"
	"use think\\Log;\n"
	"\n"
	"/**\n"
	" * Class Connection\n"
	" * @package think\n"
	" * @method Query table(string $table) 指定数据表（含前缀）\n"
	" * @method Query name(string $name) 指定数据表（不含前缀）\n"
	" *\n"
	" */\n"
	"abstract class Connection\n"
	"{\n"
	"\n"
	"    /** @var PDOStatement PDO操作实例 */\n"
	"    protected $PDOStatement;\n"
	"\n"
	"    /** @var string 当前SQL指令 */\n"
	"    protected $queryStr = \'\';\n"
	"    // 返回或者影响记录数\n"
	"    protected $numRows = 0;\n"
	"    // 事务指令数\n"
	"    protected $transTimes = 0;\n"
	"    // 错误信息\n"
	"    protected $error = \'\';\n"
	"\n"
	"    /** @var PDO[] 数据库连接ID 支持多个连接 */\n"
	"    protected $links = [];\n"
	"\n"
	"    /** @var PDO 当前连接ID */\n"
	"    protected $linkID;\n"
	"    protected $linkRead;\n"
	"    protected $linkWrite;\n"
	"\n"
	"    // 查询结果类型\n"
	"    protected $fetchType = PDO::FETCH_ASSOC;\n"
	"    // 字段属性大小写\n"
	"    protected $attrCase = PDO::CASE_LOWER;\n"
	"    // 监听回调\n"
	"    protected static $event = [];\n"
	"    // 使用Builder类\n"
	"    protected $builder;\n"
	"    // 数据库连接参数配置\n"
	"    protected $config = [\n"
	"        // 数据库类型\n"
	"        \'type\'            => \'\',\n"
	"        // 服务器地址\n"
	"        \'hostname\'        => \'\',\n"
	"        // 数据库名\n"
	"        \'database\'        => \'\',\n"
	"        // 用户名\n"
	"        \'username\'        => \'\',\n"
	"        // 密码\n"
	"        \'password\'        => \'\',\n"
	"        // 端口\n"
	"        \'hostport\'        => \'\',\n"
	"        // 连接dsn\n"
	"        \'dsn\'             => \'\',\n"
	"        // 数据库连接参数\n"
	"        \'params\'          => [],\n"
	"        // 数据库编码默认采用utf8\n"
	"        \'charset\'         => \'utf8\',\n"
	"        // 数据库表前缀\n"
	"        \'prefix\'          => \'\',\n"
	"        // 数据库调试模式\n"
	"        \'debug\'           => false,\n"
	"        // 数据库部署方式:0 集中式(单一服务器),1 分布式(主从服务器)\n"
	"        \'deploy\'          => 0,\n"
	"        // 数据库读写是否分离 主从式有效\n"
	"        \'rw_separate\'     => false,\n"
	"        // 读写分离后 主服务器数量\n"
	"        \'master_num\'      => 1,\n"
	"        // 指定从服务器序号\n"
	"        \'slave_no\'        => \'\',\n"
	"        // 模型写入后自动读取主服务器\n"
	"        \'read_master\'     => false,\n"
	"        // 是否严格检查字段是否存在\n"
	"        \'fields_strict\'   => true,\n"
	"        // 数据返回类型\n"
	"        \'result_type\'     => PDO::FETCH_ASSOC,\n"
	"        // 数据集返回类型\n"
	"        \'resultset_type\'  => \'array\',\n"
	"        // 自动写入时间戳字段\n"
	"        \'auto_timestamp\'  => false,\n"
	"        // 时间字段取出后的默认时间格式\n"
	"        \'datetime_format\' => \'Y-m-d H:i:s\',\n"
	"        // 是否需要进行SQL性能分析\n"
	"        \'sql_explain\'     => false,\n"
	"        // Builder类\n"
	"        \'builder\'         => \'\',\n"
	"        // Query类\n"
	"        \'query\'           => \'\\\\think\\\\db\\\\Query\',\n"
	"        // 是否需要断线重连\n"
	"        \'break_reconnect\' => false,\n"
	"    ];\n"
	"\n"
	"    // PDO连接参数\n"
	"    protected $params = [\n"
	"        PDO::ATTR_CASE              => PDO::CASE_NATURAL,\n"
	"        PDO::ATTR_ERRMODE           => PDO::ERRMODE_EXCEPTION,\n"
	"        PDO::ATTR_ORACLE_NULLS      => PDO::NULL_NATURAL,\n"
	"        PDO::ATTR_STRINGIFY_FETCHES => false,\n"
	"        PDO::ATTR_EMULATE_PREPARES  => false,\n"
	"    ];\n"
	"\n"
	"    // 绑定参数\n"
	"    protected $bind = [];\n"
	"\n"
	"    /**\n"
	"     * 构造函数 读取数据库配置信息\n"
	"     * @access public\n"
	"     * @param array $config 数据库配置数组\n"
	"     */\n"
	"    public function __construct(array $config = [])\n"
	"    {\n"
	"        if (!empty($config)) {\n"
	"            $this->config = array_merge($this->config, $config);\n"
	"        }\n"
	"    }\n"
	"\n"
	"    /**\n"
	"     * 获取新的查询对象\n"
	"     * @access protected\n"
	"     * @return Query\n"
	"     */\n"
	"    protected function getQuery()\n"
	"    {\n"
	"        $class = $this->config[\'query\'];\n"
	"        return new $class($this);\n"
	"    }\n"
	"\n"
	"    /**\n"
	"     * 获取当前连接器类对应的Builder类\n"
	"     * @access public\n"
	"     * @return string\n"
	"     */\n"
	"    public function getBuilder()\n"
	"    {\n"
	"        if (!empty($this->builder)) {\n"
	"            return $this->builder;\n"
	"        } else {\n"
	"            return $this->getConfig(\'builder\') ?: \'\\\\think\\\\db\\\\builder\\\\\' . ucfirst($this->getConfig(\'type\'));\n"
	"        }\n"
	"    }\n"
	"\n"
	"    /**\n"
	"     * 调用Query类的查询方法\n"
	"     * @access public\n"
	"     * @param string    $method 方法名称\n"
	"     * @param array     $args 调用参数\n"
	"     * @return mixed\n"
	"     */\n"
	"    public function __call($method, $args)\n"
	"    {\n"
	"        return call_user_func_array([$this->getQuery(), $method], $args);\n"
	"    }\n"
	"\n"
	"    /**\n"
	"     * 解析pdo连接的dsn信息\n"
	"     * @access protected\n"
	"     * @param array $config 连接信息\n"
	"     * @return string\n"
	"     */\n"
	"    abstract protected function parseDsn($config);\n"
	"\n"
	"    /**\n"
	"     * 取得数据表的字段信息\n"
	"     * @access public\n"
	"     * @param string $tableName\n"
	"     * @return array\n"
	"     */\n"
	"    abstract public function getFields($tableName);\n"
	"\n"
	"    /**\n"
	"     * 取得数据库的表信息\n"
	"     * @access public\n"
	"     * @param string $dbName\n"
	"     * @return array\n"
	"     */\n"
	"    abstract public function getTables($dbName);\n"
	"\n"
	"    /**\n"
	"     * SQL性能分析\n"
	"     * @access protected\n"
	"     * @param string $sql\n"
	"     * @return array\n"
	"     */\n"
	"    abstract protected function getExplain($sql);\n"
	"\n"
	"    /**\n"
	"     * 对返数据表字段信息进行大小写转换出来\n"
	"     * @access public\n"
	"     * @param array $info 字段信息\n"
	"     * @return array\n"
	"     */\n"
	"    public function fieldCase($info)\n"
	"    {\n"
	"        // 字段大小写转换\n"
	"        switch ($this->attrCase) {\n"
	"            case PDO::CASE_LOWER:\n"
	"                $info = array_change_key_case($info);\n"
	"                break;\n"
	"            case PDO::CASE_UPPER:\n"
	"                $info = array_change_key_case($info, CASE_UPPER);\n"
	"                break;\n"
	"            case PDO::CASE_NATURAL:\n"
	"            default:\n"
	"                // 不做转换\n"
	"        }\n"
	"        return $info;\n"
	"    }\n"
	"\n"
	"    /**\n"
	"     * 获取数据库的配置参数\n"
	"     * @access public\n"
	"     * @param string $config 配置名称\n"
	"     * @return mixed\n"
	"     */\n"
	"    public function getConfig($config = \'\')\n"
	"    {\n"
	"        return $config ? $this->config[$config] : $this->config;\n"
	"    }\n"
	"\n"
	"    /**\n"
	"     * 设置数据库的配置参数\n"
	"     * @access public\n"
	"     * @param string|array      $config 配置名称\n"
	"     * @param mixed             $value 配置值\n"
	"     * @return void\n"
	"     */\n"
	"    public function setConfig($config, $value = \'\')\n"
	"    {\n"
	"        if (is_array($config)) {\n"
	"            $this->config = array_merge($this->config, $config);\n"
	"        } else {\n"
	"            $this->config[$config] = $value;\n"
	"        }\n"
	"    }\n"
	"\n"
	"    /**\n"
	"     * 连接数据库方法\n"
	"     * @access public\n"
	"     * @param array         $config 连接参数\n"
	"     * @param integer       $linkNum 连接序号\n"
	"     * @param array|bool    $autoConnection 是否自动连接主数据库（用于分布式）\n"
	"     * @return PDO\n"
	"     * @throws Exception\n"
	"     */\n"
	"    public function connect(array $config = [], $linkNum = 0, $autoConnection = false)\n"
	"    {\n"
	"        if (!isset($this->links[$linkNum])) {\n"
	"            if (!$config) {\n"
	"                $config = $this->config;\n"
	"            } else {\n"
	"                $config = array_merge($this->config, $config);\n"
	"            }\n"
	"            // 连接参数\n"
	"            if (isset($config[\'params\']) && is_array($config[\'params\'])) {\n"
	"                $params = $config[\'params\'] + $this->params;\n"
	"            } else {\n"
	"                $params = $this->params;\n"
	"            }\n"
	"            // 记录当前字段属性大小写设置\n"
	"            $this->attrCase = $params[PDO::ATTR_CASE];\n"
	"\n"
	"            // 数据返回类型\n"
	"            if (isset($config[\'result_type\'])) {\n"
	"                $this->fetchType = $config[\'result_type\'];\n"
	"            }\n"
	"            try {\n"
	"                if (empty($config[\'dsn\'])) {\n"
	"                    $config[\'dsn\'] = $this->parseDsn($config);\n"
	"                }\n"
	"                if ($config[\'debug\']) {\n"
	"                    $startTime = microtime(true);\n"
	"                }\n"
	"                $this->links[$linkNum] = new PDO($config[\'dsn\'], $config[\'username\'], $config[\'password\'], $params);\n"
	"                if ($config[\'debug\']) {\n"
	"                    // 记录数据库连接信息\n"
	"                    Log::record(\'[ DB ] CONNECT:[ UseTime:\' . number_format(microtime(true) - $startTime, 6) . \'s ] \' . $config[\'dsn\'], \'sql\');\n"
	"                }\n"
	"            } catch (\\PDOException $e) {\n"
	"                if ($autoConnection) {\n"
	"                    Log::record($e->getMessage(), \'error\');\n"
	"                    return $this->connect($autoConnection, $linkNum);\n"
	"                } else {\n"
	"                    throw $e;\n"
	"                }\n"
	"            }\n"
	"        }\n"
	"        return $this->links[$linkNum];\n"
	"    }\n"
	"\n"
	"    /**\n"
	"     * 释放查询结果\n"
	"     * @access public\n"
	"     */\n"
	"    public function free()\n"
	"    {\n"
	"        $this->PDOStatement = null;\n"
	"    }\n"
	"\n"
	"    /**\n"
	"     * 获取PDO对象\n"
	"     * @access public\n"
	"     * @return \\PDO|false\n"
	"     */\n"
	"    public function getPdo()\n"
	"    {\n"
	"        if (!$this->linkID) {\n"
	"            return false;\n"
	"        } else {\n"
	"            return $this->linkID;\n"
	"        }\n"
	"    }\n"
	"\n"
	"    /**\n"
	"     * 执行查询 返回数据集\n"
	"     * @access public\n"
	"     * @param string        $sql sql指令\n"
	"     * @param array         $bind 参数绑定\n"
	"     * @param bool          $master 是否在主服务器读操作\n"
	"     * @param bool          $pdo 是否返回PDO对象\n"
	"     * @return mixed\n"
	"     * @throws PDOException\n"
	"     * @throws \\Exception\n"
	"     */\n"
	"    public function query($sql, $bind = [], $master = false, $pdo = false)\n"
	"    {\n"
	"        $this->initConnect($master);\n"
	"        if (!$this->linkID) {\n"
	"            return false;\n"
	"        }\n"
	"\n"
	"        // 记录SQL语句\n"
	"        $this->queryStr = $sql;\n"
	"        if ($bind) {\n"
	"            $this->bind = $bind;\n"
	"        }\n"
	"\n"
	"        Db::$queryTimes++;\n"
	"        try {\n"
	"            // 调试开始\n"
	"            $this->debug(true);\n"
	"\n"
	"            // 预处理\n"
	"            $this->PDOStatement = $this->linkID->prepare($sql);\n"
	"\n"
	"            // 是否为存储过程调用\n"
	"            $procedure = in_array(strtolower(substr(trim($sql), 0, 4)), [\'call\', \'exec\']);\n"
	"            // 参数绑定\n"
	"            if ($procedure) {\n"
	"                $this->bindParam($bind);\n"
	"            } else {\n"
	"                $this->bindValue($bind);\n"
	"            }\n"
	"            // 执行查询\n"
	"            $this->PDOStatement->execute();\n"
	"            // 调试结束\n"
	"            $this->debug(false, \'\', $master);\n"
	"            // 返回结果集\n"
	"            return $this->getResult($pdo, $procedure);\n"
	"        } catch (\\PDOException $e) {\n"
	"            if ($this->isBreak($e)) {\n"
	"                return $this->close()->query($sql, $bind, $master, $pdo);\n"
	"            }\n"
	"            throw new PDOException($e, $this->config, $this->getLastsql());\n"
	"        } catch (\\Throwable $e) {\n"
	"            if ($this->isBreak($e)) {\n"
	"                return $this->close()->query($sql, $bind, $master, $pdo);\n"
	"            }\n"
	"            throw $e;\n"
	"        } catch (\\Exception $e) {\n"
	"            if ($this->isBreak($e)) {\n"
	"                return $this->close()->query($sql, $bind, $master, $pdo);\n"
	"            }\n"
	"            throw $e;\n"
	"        }\n"
	"    }\n"
	"\n"
	"    /**\n"
	"     * 执行语句\n"
	"     * @access public\n"
	"     * @param  string        $sql sql指令\n"
	"     * @param  array         $bind 参数绑定\n"
	"     * @param  Query         $query 查询对象\n"
	"     * @return int\n"
	"     * @throws PDOException\n"
	"     * @throws \\Exception\n"
	"     */\n"
	"    public function execute($sql, $bind = [], Query $query = null)\n"
	"    {\n"
	"        $this->initConnect(true);\n"
	"        if (!$this->linkID) {\n"
	"            return false;\n"
	"        }\n"
	"\n"
	"        // 记录SQL语句\n"
	"        $this->queryStr = $sql;\n"
	"        if ($bind) {\n"
	"            $this->bind = $bind;\n"
	"        }\n"
	"\n"
	"        Db::$executeTimes++;\n"
	"        try {\n"
	"            // 调试开始\n"
	"            $this->debug(true);\n"
	"\n"
	"            // 预处理\n"
	"            $this->PDOStatement = $this->linkID->prepare($sql);\n"
	"\n"
	"            // 是否为存储过程调用\n"
	"            $procedure = in_array(strtolower(substr(trim($sql), 0, 4)), [\'call\', \'exec\']);\n"
	"            // 参数绑定\n"
	"            if ($procedure) {\n"
	"                $this->bindParam($bind);\n"
	"            } else {\n"
	"                $this->bindValue($bind);\n"
	"            }\n"
	"            // 执行语句\n"
	"            $this->PDOStatement->execute();\n"
	"            // 调试结束\n"
	"            $this->debug(false, \'\', true);\n"
	"\n"
	"            if ($query && !empty($this->config[\'deploy\']) && !empty($this->config[\'read_master\'])) {\n"
	"                $query->readMaster();\n"
	"            }\n"
	"\n"
	"            $this->numRows = $this->PDOStatement->rowCount();\n"
	"            return $this->numRows;\n"
	"        } catch (\\PDOException $e) {\n"
	"            if ($this->isBreak($e)) {\n"
	"                return $this->close()->execute($sql, $bind, $query);\n"
	"            }\n"
	"            throw new PDOException($e, $this->config, $this->getLastsql());\n"
	"        } catch (\\Throwable $e) {\n"
	"            if ($this->isBreak($e)) {\n"
	"                return $this->close()->execute($sql, $bind, $query);\n"
	"            }\n"
	"            throw $e;\n"
	"        } catch (\\Exception $e) {\n"
	"            if ($this->isBreak($e)) {\n"
	"                return $this->close()->execute($sql, $bind, $query);\n"
	"            }\n"
	"            throw $e;\n"
	"        }\n"
	"    }\n"
	"\n"
	"    /**\n"
	"     * 根据参数绑定组装最终的SQL语句 便于调试\n"
	"     * @access public\n"
	"     * @param string    $sql 带参数绑定的sql语句\n"
	"     * @param array     $bind 参数绑定列表\n"
	"     * @return string\n"
	"     */\n"
	"    public function getRealSql($sql, array $bind = [])\n"
	"    {\n"
	"        if (is_array($sql)) {\n"
	"            $sql = implode(\';\', $sql);\n"
	"        }\n"
	"\n"
	"        foreach ($bind as $key => $val) {\n"
	"            $value = is_array($val) ? $val[0] : $val;\n"
	"            $type  = is_array($val) ? $val[1] : PDO::PARAM_STR;\n"
	"            if (PDO::PARAM_STR == $type) {\n"
	"                $value = $this->quote($value);\n"
	"            } elseif (PDO::PARAM_INT == $type) {\n"
	"                $value = (float) $value;\n"
	"            }\n"
	"            // 判断占位符\n"
	"            $sql = is_numeric($key) ?\n"
	"            substr_replace($sql, $value, strpos($sql, \'?\'), 1) :\n"
	"            str_replace(\n"
	"                [\':\' . $key . \')\', \':\' . $key . \',\', \':\' . $key . \' \', \':\' . $key . PHP_EOL],\n"
	"                [$value . \')\', $value . \',\', $value . \' \', $value . PHP_EOL],\n"
	"                $sql . \' \');\n"
	"        }\n"
	"        return rtrim($sql);\n"
	"    }\n"
	"\n"
	"    /**\n"
	"     * 参数绑定\n"
	"     * 支持 [\'name\'=>\'value\',\'id\'=>123] 对应命名占位符\n"
	"     * 或者 [\'value\',123] 对应问号占位符\n"
	"     * @access public\n"
	"     * @param array $bind 要绑定的参数列表\n"
	"     * @return void\n"
	"     * @throws BindParamException\n"
	"     */\n"
	"    protected function bindValue(array $bind = [])\n"
	"    {\n"
	"        foreach ($bind as $key => $val) {\n"
	"            // 占位符\n"
	"            $param = is_numeric($key) ? $key + 1 : \':\' . $key;\n"
	"            if (is_array($val)) {\n"
	"                if (PDO::PARAM_INT == $val[1] && \'\' === $val[0]) {\n"
	"                    $val[0] = 0;\n"
	"                }\n"
	"                $result = $this->PDOStatement->bindValue($param, $val[0], $val[1]);\n"
	"            } else {\n"
	"                $result = $this->PDOStatement->bindValue($param, $val);\n"
	"            }\n"
	"            if (!$result) {\n"
	"                throw new BindParamException(\n"
	"                    \"Error occurred  when binding parameters \'{$param}\'\",\n"
	"                    $this->config,\n"
	"                    $this->getLastsql(),\n"
	"                    $bind\n"
	"                );\n"
	"            }\n"
	"        }\n"
	"    }\n"
	"\n"
	"    /**\n"
	"     * 存储过程的输入输出参数绑定\n"
	"     * @access public\n"
	"     * @param array $bind 要绑定的参数列表\n"
	"     * @return void\n"
	"     * @throws BindParamException\n"
	"     */\n"
	"    protected function bindParam($bind)\n"
	"    {\n"
	"        foreach ($bind as $key => $val) {\n"
	"            $param = is_numeric($key) ? $key + 1 : \':\' . $key;\n"
	"            if (is_array($val)) {\n"
	"                array_unshift($val, $param);\n"
	"                $result = call_user_func_array([$this->PDOStatement, \'bindParam\'], $val);\n"
	"            } else {\n"
	"                $result = $this->PDOStatement->bindValue($param, $val);\n"
	"            }\n"
	"            if (!$result) {\n"
	"                $param = array_shift($val);\n"
	"                throw new BindParamException(\n"
	"                    \"Error occurred  when binding parameters \'{$param}\'\",\n"
	"                    $this->config,\n"
	"                    $this->getLastsql(),\n"
	"                    $bind\n"
	"                );\n"
	"            }\n"
	"        }\n"
	"    }\n"
	"\n"
	"    /**\n"
	"     * 获得数据集数组\n"
	"     * @access protected\n"
	"     * @param bool   $pdo 是否返回PDOStatement\n"
	"     * @param bool   $procedure 是否存储过程\n"
	"     * @return PDOStatement|array\n"
	"     */\n"
	"    protected function getResult($pdo = false, $procedure = false)\n"
	"    {\n"
	"        if ($pdo) {\n"
	"            // 返回PDOStatement对象处理\n"
	"            return $this->PDOStatement;\n"
	"        }\n"
	"        if ($procedure) {\n"
	"            // 存储过程返回结果\n"
	"            return $this->procedure();\n"
	"        }\n"
	"        $result        = $this->PDOStatement->fetchAll($this->fetchType);\n"
	"        $this->numRows = count($result);\n"
	"        return $result;\n"
	"    }\n"
	"\n"
	"    /**\n"
	"     * 获得存储过程数据集\n"
	"     * @access protected\n"
	"     * @return array\n"
	"     */\n"
	"    protected function procedure()\n"
	"    {\n"
	"        $item = [];\n"
	"        do {\n"
	"            $result = $this->getResult();\n"
	"            if ($result) {\n"
	"                $item[] = $result;\n"
	"            }\n"
	"        } while ($this->PDOStatement->nextRowset());\n"
	"        $this->numRows = count($item);\n"
	"        return $item;\n"
	"    }\n"
	"\n"
	"    /**\n"
	"     * 执行数据库事务\n"
	"     * @access public\n"
	"     * @param callable $callback 数据操作方法回调\n"
	"     * @return mixed\n"
	"     * @throws PDOException\n"
	"     * @throws \\Exception\n"
	"     * @throws \\Throwable\n"
	"     */\n"
	"    public function transaction($callback)\n"
	"    {\n"
	"        $this->startTrans();\n"
	"        try {\n"
	"            $result = null;\n"
	"            if (is_callable($callback)) {\n"
	"                $result = call_user_func_array($callback, [$this]);\n"
	"            }\n"
	"            $this->commit();\n"
	"            return $result;\n"
	"        } catch (\\Exception $e) {\n"
	"            $this->rollback();\n"
	"            throw $e;\n"
	"        } catch (\\Throwable $e) {\n"
	"            $this->rollback();\n"
	"            throw $e;\n"
	"        }\n"
	"    }\n"
	"\n"
	"    /**\n"
	"     * 启动事务\n"
	"     * @access public\n"
	"     * @return bool|mixed\n"
	"     * @throws \\Exception\n"
	"     */\n"
	"    public function startTrans()\n"
	"    {\n"
	"        $this->initConnect(true);\n"
	"        if (!$this->linkID) {\n"
	"            return false;\n"
	"        }\n"
	"\n"
	"        ++$this->transTimes;\n"
	"        try {\n"
	"            if (1 == $this->transTimes) {\n"
	"                $this->linkID->beginTransaction();\n"
	"            } elseif ($this->transTimes > 1 && $this->supportSavepoint()) {\n"
	"                $this->linkID->exec(\n"
	"                    $this->parseSavepoint(\'trans\' . $this->transTimes)\n"
	"                );\n"
	"            }\n"
	"\n"
	"        } catch (\\Exception $e) {\n"
	"            if ($this->isBreak($e)) {\n"
	"                --$this->transTimes;\n"
	"                return $this->close()->startTrans();\n"
	"            }\n"
	"            throw $e;\n"
	"        } catch (\\Error $e) {\n"
	"            if ($this->isBreak($e)) {\n"
	"                --$this->transTimes;\n"
	"                return $this->close()->startTrans();\n"
	"            }\n"
	"            throw $e;\n"
	"        }\n"
	"    }\n"
	"\n"
	"    /**\n"
	"     * 用于非自动提交状态下面的查询提交\n"
	"     * @access public\n"
	"     * @return void\n"
	"     * @throws PDOException\n"
	"     */\n"
	"    public function commit()\n"
	"    {\n"
	"        $this->initConnect(true);\n"
	"\n"
	"        if (1 == $this->transTimes) {\n"
	"            $this->linkID->commit();\n"
	"        }\n"
	"\n"
	"        --$this->transTimes;\n"
	"    }\n"
	"\n"
	"    /**\n"
	"     * 事务回滚\n"
	"     * @access public\n"
	"     * @return void\n"
	"     * @throws PDOException\n"
	"     */\n"
	"    public function rollback()\n"
	"    {\n"
	"        $this->initConnect(true);\n"
	"\n"
	"        if (1 == $this->transTimes) {\n"
	"            $this->linkID->rollBack();\n"
	"        } elseif ($this->transTimes > 1 && $this->supportSavepoint()) {\n"
	"            $this->linkID->exec(\n"
	"                $this->parseSavepointRollBack(\'trans\' . $this->transTimes)\n"
	"            );\n"
	"        }\n"
	"\n"
	"        $this->transTimes = max(0, $this->transTimes - 1);\n"
	"    }\n"
	"\n"
	"    /**\n"
	"     * 是否支持事务嵌套\n"
	"     * @return bool\n"
	"     */\n"
	"    protected function supportSavepoint()\n"
	"    {\n"
	"        return false;\n"
	"    }\n"
	"\n"
	"    /**\n"
	"     * 生成定义保存点的SQL\n"
	"     * @param $name\n"
	"     * @return string\n"
	"     */\n"
	"    protected function parseSavepoint($name)\n"
	"    {\n"
	"        return \'SAVEPOINT \' . $name;\n"
	"    }\n"
	"\n"
	"    /**\n"
	"     * 生成回滚到保存点的SQL\n"
	"     * @param $name\n"
	"     * @return string\n"
	"     */\n"
	"    protected function parseSavepointRollBack($name)\n"
	"    {\n"
	"        return \'ROLLBACK TO SAVEPOINT \' . $name;\n"
	"    }\n"
	"\n"
	"    /**\n"
	"     * 批处理执行SQL语句\n"
	"     * 批处理的指令都认为是execute操作\n"
	"     * @access public\n"
	"     * @param array $sqlArray SQL批处理指令\n"
	"     * @return boolean\n"
	"     */\n"
	"    public function batchQuery($sqlArray = [], $bind = [], Query $query = null)\n"
	"    {\n"
	"        if (!is_array($sqlArray)) {\n"
	"            return false;\n"
	"        }\n"
	"        // 自动启动事务支持\n"
	"        $this->startTrans();\n"
	"        try {\n"
	"            foreach ($sqlArray as $sql) {\n"
	"                $this->execute($sql, $bind, $query);\n"
	"            }\n"
	"            // 提交事务\n"
	"            $this->commit();\n"
	"        } catch (\\Exception $e) {\n"
	"            $this->rollback();\n"
	"            throw $e;\n"
	"        }\n"
	"\n"
	"        return true;\n"
	"    }\n"
	"\n"
	"    /**\n"
	"     * 获得查询次数\n"
	"     * @access public\n"
	"     * @param boolean $execute 是否包含所有查询\n"
	"     * @return integer\n"
	"     */\n"
	"    public function getQueryTimes($execute = false)\n"
	"    {\n"
	"        return $execute ? Db::$queryTimes + Db::$executeTimes : Db::$queryTimes;\n"
	"    }\n"
	"\n"
	"    /**\n"
	"     * 获得执行次数\n"
	"     * @access public\n"
	"     * @return integer\n"
	"     */\n"
	"    public function getExecuteTimes()\n"
	"    {\n"
	"        return Db::$executeTimes;\n"
	"    }\n"
	"\n"
	"    /**\n"
	"     * 关闭数据库（或者重新连接）\n"
	"     * @access public\n"
	"     * @return $this\n"
	"     */\n"
	"    public function close()\n"
	"    {\n"
	"        $this->linkID    = null;\n"
	"        $this->linkWrite = null;\n"
	"        $this->linkRead  = null;\n"
	"        $this->links     = [];\n"
	"        // 释放查询\n"
	"        $this->free();\n"
	"        return $this;\n"
	"    }\n"
	"\n"
	"    /**\n"
	"     * 是否断线\n"
	"     * @access protected\n"
	"     * @param \\PDOException|\\Exception  $e 异常对象\n"
	"     * @return bool\n"
	"     */\n"
	"    protected function isBreak($e)\n"
	"    {\n"
	"        if (!$this->config[\'break_reconnect\']) {\n"
	"            return false;\n"
	"        }\n"
	"\n"
	"        $info = [\n"
	"            \'server has gone away\',\n"
	"            \'no connection to the server\',\n"
	"            \'Lost connection\',\n"
	"            \'is dead or not enabled\',\n"
	"            \'Error while sending\',\n"
	"            \'decryption failed or bad record mac\',\n"
	"            \'server closed the connection unexpectedly\',\n"
	"            \'SSL connection has been closed unexpectedly\',\n"
	"            \'Error writing data to the connection\',\n"
	"            \'Resource deadlock avoided\',\n"
	"            \'failed with errno\',\n"
	"        ];\n"
	"\n"
	"        $error = $e->getMessage();\n"
	"\n"
	"        foreach ($info as $msg) {\n"
	"            if (false !== stripos($error, $msg)) {\n"
	"                return true;\n"
	"            }\n"
	"        }\n"
	"        return false;\n"
	"    }\n"
	"\n"
	"    /**\n"
	"     * 获取最近一次查询的sql语句\n"
	"     * @access public\n"
	"     * @return string\n"
	"     */\n"
	"    public function getLastSql()\n"
	"    {\n"
	"        return $this->getRealSql($this->queryStr, $this->bind);\n"
	"    }\n"
	"\n"
	"    /**\n"
	"     * 获取最近插入的ID\n"
	"     * @access public\n"
	"     * @param string  $sequence     自增序列名\n"
	"     * @return string\n"
	"     */\n"
	"    public function getLastInsID($sequence = null)\n"
	"    {\n"
	"        return $this->linkID->lastInsertId($sequence);\n"
	"    }\n"
	"\n"
	"    /**\n"
	"     * 获取返回或者影响的记录数\n"
	"     * @access public\n"
	"     * @return integer\n"
	"     */\n"
	"    public function getNumRows()\n"
	"    {\n"
	"        return $this->numRows;\n"
	"    }\n"
	"\n"
	"    /**\n"
	"     * 获取最近的错误信息\n"
	"     * @access public\n"
	"     * @return string\n"
	"     */\n"
	"    public function getError()\n"
	"    {\n"
	"        if ($this->PDOStatement) {\n"
	"            $error = $this->PDOStatement->errorInfo();\n"
	"            $error = $error[1] . \':\' . $error[2];\n"
	"        } else {\n"
	"            $error = \'\';\n"
	"        }\n"
	"        if (\'\' != $this->queryStr) {\n"
	"            $error .= \"\\n [ SQL语句 ] : \" . $this->getLastsql();\n"
	"        }\n"
	"        return $error;\n"
	"    }\n"
	"\n"
	"    /**\n"
	"     * SQL指令安全过滤\n"
	"     * @access public\n"
	"     * @param string $str SQL字符串\n"
	"     * @param bool   $master 是否主库查询\n"
	"     * @return string\n"
	"     */\n"
	"    public function quote($str, $master = true)\n"
	"    {\n"
	"        $this->initConnect($master);\n"
	"        return $this->linkID ? $this->linkID->quote($str) : $str;\n"
	"    }\n"
	"\n"
	"    /**\n"
	"     * 数据库调试 记录当前SQL及分析性能\n"
	"     * @access protected\n"
	"     * @param boolean $start 调试开始标记 true 开始 false 结束\n"
	"     * @param string  $sql 执行的SQL语句 留空自动获取\n"
	"     * @param boolean $master 主从标记\n"
	"     * @return void\n"
	"     */\n"
	"    protected function debug($start, $sql = \'\', $master = false)\n"
	"    {\n"
	"        if (!empty($this->config[\'debug\'])) {\n"
	"            // 开启数据库调试模式\n"
	"            if ($start) {\n"
	"                Debug::remark(\'queryStartTime\', \'time\');\n"
	"            } else {\n"
	"                // 记录操作结束时间\n"
	"                Debug::remark(\'queryEndTime\', \'time\');\n"
	"                $runtime = Debug::getRangeTime(\'queryStartTime\', \'queryEndTime\');\n"
	"                $sql     = $sql ?: $this->getLastsql();\n"
	"                $result  = [];\n"
	"                // SQL性能分析\n"
	"                if ($this->config[\'sql_explain\'] && 0 === stripos(trim($sql), \'select\')) {\n"
	"                    $result = $this->getExplain($sql);\n"
	"                }\n"
	"                // SQL监听\n"
	"                $this->trigger($sql, $runtime, $result, $master);\n"
	"            }\n"
	"        }\n"
	"    }\n"
	"\n"
	"    /**\n"
	"     * 监听SQL执行\n"
	"     * @access public\n"
	"     * @param callable $callback 回调方法\n"
	"     * @return void\n"
	"     */\n"
	"    public function listen($callback)\n"
	"    {\n"
	"        self::$event[] = $callback;\n"
	"    }\n"
	"\n"
	"    /**\n"
	"     * 触发SQL事件\n"
	"     * @access protected\n"
	"     * @param string    $sql SQL语句\n"
	"     * @param float     $runtime SQL运行时间\n"
	"     * @param mixed     $explain SQL分析\n"
	"     * @param  bool     $master 主从标记\n"
	"     * @return void\n"
	"     */\n"
	"    protected function trigger($sql, $runtime, $explain = [], $master = false)\n"
	"    {\n"
	"        if (!empty(self::$event)) {\n"
	"            foreach (self::$event as $callback) {\n"
	"                if (is_callable($callback)) {\n"
	"                    call_user_func_array($callback, [$sql, $runtime, $explain, $master]);\n"
	"                }\n"
	"            }\n"
	"        } else {\n"
	"            // 未注册监听则记录到日志中\n"
	"            if ($this->config[\'deploy\']) {\n"
	"                // 分布式记录当前操作的主从\n"
	"                $master = $master ? \'master|\' : \'slave|\';\n"
	"            } else {\n"
	"                $master = \'\';\n"
	"            }\n"
	"\n"
	"            Log::record(\'[ SQL ] \' . $sql . \' [ \' . $master . \'RunTime:\' . $runtime . \'s ]\', \'sql\');\n"
	"            if (!empty($explain)) {\n"
	"                Log::record(\'[ EXPLAIN : \' . var_export($explain, true) . \' ]\', \'sql\');\n"
	"            }\n"
	"        }\n"
	"    }\n"
	"\n"
	"    /**\n"
	"     * 初始化数据库连接\n"
	"     * @access protected\n"
	"     * @param boolean $master 是否主服务器\n"
	"     * @return void\n"
	"     */\n"
	"    protected function initConnect($master = true)\n"
	"    {\n"
	"        if (!empty($this->config[\'deploy\'])) {\n"
	"            // 采用分布式数据库\n"
	"            if ($master || $this->transTimes) {\n"
	"                if (!$this->linkWrite) {\n"
	"                    $this->linkWrite = $this->multiConnect(true);\n"
	"                }\n"
	"                $this->linkID = $this->linkWrite;\n"
	"            } else {\n"
	"                if (!$this->linkRead) {\n"
	"                    $this->linkRead = $this->multiConnect(false);\n"
	"                }\n"
	"                $this->linkID = $this->linkRead;\n"
	"            }\n"
	"        } elseif (!$this->linkID) {\n"
	"            // 默认单数据库\n"
	"            $this->linkID = $this->connect();\n"
	"        }\n"
	"    }\n"
	"\n"
	"    /**\n"
	"     * 连接分布式服务器\n"
	"     * @access protected\n"
	"     * @param boolean $master 主服务器\n"
	"     * @return PDO\n"
	"     */\n"
	"    protected function multiConnect($master = false)\n"
	"    {\n"
	"        $_config = [];\n"
	"        // 分布式数据库配置解析\n"
	"        foreach ([\'username\', \'password\', \'hostname\', \'hostport\', \'database\', \'dsn\', \'charset\'] as $name) {\n"
	"            $_config[$name] = explode(\',\', $this->config[$name]);\n"
	"        }\n"
	"\n"
	"        // 主服务器序号\n"
	"        $m = floor(mt_rand(0, $this->config[\'master_num\'] - 1));\n"
	"\n"
	"        if ($this->config[\'rw_separate\']) {\n"
	"            // 主从式采用读写分离\n"
	"            if ($master) // 主服务器写入\n"
	"            {\n"
	"                $r = $m;\n"
	"            } elseif (is_numeric($this->config[\'slave_no\'])) {\n"
	"                // 指定服务器读\n"
	"                $r = $this->config[\'slave_no\'];\n"
	"            } else {\n"
	"                // 读操作连接从服务器 每次随机连接的数据库\n"
	"                $r = floor(mt_rand($this->config[\'master_num\'], count($_config[\'hostname\']) - 1));\n"
	"            }\n"
	"        } else {\n"
	"            // 读写操作不区分服务器 每次随机连接的数据库\n"
	"            $r = floor(mt_rand(0, count($_config[\'hostname\']) - 1));\n"
	"        }\n"
	"        $dbMaster = false;\n"
	"        if ($m != $r) {\n"
	"            $dbMaster = [];\n"
	"            foreach ([\'username\', \'password\', \'hostname\', \'hostport\', \'database\', \'dsn\', \'charset\'] as $name) {\n"
	"                $dbMaster[$name] = isset($_config[$name][$m]) ? $_config[$name][$m] : $_config[$name][0];\n"
	"            }\n"
	"        }\n"
	"        $dbConfig = [];\n"
	"        foreach ([\'username\', \'password\', \'hostname\', \'hostport\', \'database\', \'dsn\', \'charset\'] as $name) {\n"
	"            $dbConfig[$name] = isset($_config[$name][$r]) ? $_config[$name][$r] : $_config[$name][0];\n"
	"        }\n"
	"        return $this->connect($dbConfig, $r, $r == $m ? false : $dbMaster);\n"
	"    }\n"
	"\n"
	"    /**\n"
	"     * 析构方法\n"
	"     * @access public\n"
	"     */\n"
	"    public function __destruct()\n"
	"    {\n"
	"        // 释放查询\n"
	"        if ($this->PDOStatement) {\n"
	"            $this->free();\n"
	"        }\n"
	"        // 关闭连接\n"
	"        $this->close();\n"
	"    }\n"
	"}\n"
	"\n"
;
#endif